﻿//////////////////////////////////////////////////////////////////////////////
//
//  Copyright © 1998-2024 Glodon Company Limited.  All rights reserved.
//
//  Use of this software is subject to the terms of the Glodon license
//  agreement provided at the time of installation or download, or which
//  otherwise accompanies this software in either electronic or hard copy form.  
//
//////////////////////////////////////////////////////////////////////////////


#pragma once
#include "GcmpDatabase.h"
#include "GbmpCommon.h"
#include "NdbClassSchema.h"
#include "NdbClassSchemaChange.h"
#include "NdbClassSchemaLibraryOfFile.h"
#include <mutex>
#include "GbmpWindows.h"

#pragma warning( push )
#pragma warning( disable : 4251 )

namespace gcmp
{
    class ClassId;

    /// 升级管理器。每次打开文件时会进行升级
    class GCMP_DATABASE_EXPORT NdbUpgradeManager
    {
        CANNOT_COPY_OR_ASSIGN(NdbUpgradeManager);
    public:
        /**
        *@brief     获取此单例对象
        */
        static NdbUpgradeManager * GetInstance();

        /// \brief 从IOStream反流ClassSchema，并保存
        SchemaError UnserializeClassSchemas(IIOStream & ioStream, std::wstring* errorMessage);

        /**
        *@brief     在任何一次升级发生前调用，检查类升级是否合法。比如是否有跳过的版本，升级的声明是否与当前代码实际情况不一致等等。
        */
        bool Check(bool classHierarchyBuilt) const;

        /**
        *@brief     收集代码中所有类schema的所有版本的改变
        *@note      通过类升级宏的声明在程序启动时自动调用，不要手动调用
        */
        void InsertClassSchemaChange(const ClassId & classId, const NdbClassSchemaChange& classSchemaChange);

        /**
        *@brief     获取当前正在升级的文件的指定类的schema
        *@note      需要在BuildClassSchemasOfFile()调用完之后调用，时间复杂度是O(1)；否则请使用NdbClassSchema::GetSchema()以O(n)的时间复杂度获取
        */
        const NdbClassSchema* GetClassSchemaOfFile(const ClassId & classId) const;

        // 每次升级文件前初始化
        void InitUpgradeFile(UInt32 fileMajorVersion);

        // 克隆文件的ClassSchemaLibrary。克隆的Library里Class Hierarchy已构建好。
        gcmp::OwnerPtr<NdbClassSchemaLibraryOfFile> CloneClassScheamLibraryOfFile() const;

        // 获取文件的ClassSchemaLibrary。第一次调用时构建Class Hierarchy。
        const NdbClassSchemaLibraryOfFile * GetClassScheamLibraryOfFile();

        NdbClassSchemaLibraryOfFile * GetClassScheamLibraryOfFileFW();
        // 按照索引获取classId
        bool GetClassIdByIndex(UInt32 index, ClassId & classId) const;

        // 加入一个当前正在升级的文件的类schema
        void InsertClassSchemaOfFile(OwnerPtr<NdbClassSchema> opClassSchema);

        // 查询指定类的最新版本号
        UInt32 GetLatestVersion(const ClassId & classId) const;

        // 找到指定类的父类的指定版本的改变
        const NdbParentClassChange * GetParentClassChange(const ClassId & classId, UInt32 newVersion) const;

        // 根据类，版本号，属性改变类型获取该类的该次升级的符合该属性改变类型的所有属性改变
        std::vector<const NdbPropertyChangeBase *> GetPropertyChangeByType(const ClassId & classId, UInt32 newVersion, EnPropertyChangeType propChangeType) const;

        // 获取类的指定升级版本的所有用户自定义对象内升级例程
        std::vector<const NdbCustomizedUpgrader *> GetCustomizedUpgraders(const ClassId & classId, UInt32 newVersion) const;

        // 获取指定版本属性在另一个版本中的名字。如果传入的srcName在srcVersion中不存在，返回false。
        bool GetPropertyName(
            const ClassId & classId,
            UInt32 srcVersion,
            UInt32 dstVersion,
            const std::wstring& srcName,
            std::wstring& dstName
        ) const;

    private:
        NdbUpgradeManager(){}

        // 在获取当前正在升级的文件的所有类schema之后，需要构建起相互关系
        void BuildClassSchemasOfFile();

        // 查询属性在一次ClassSchemaChange后变成了什么名字。
        bool GetNewPropertyNameAfterOneClassSchemaChange(
            const NdbClassSchemaChange& classSchemaChange,
            const std::wstring& beforeName,
            std::wstring& afterName
        ) const;

        // 反向查询属性在一次ClassSchemaChange前是什么名字。
        bool GetOldPropertyNameBeforeOneClassSchemaChange(
            const NdbClassSchemaChange& classSchemaChange,
            const std::wstring& afterName,
            std::wstring& beforeName
        ) const;

        typedef std::set<NdbClassSchemaChange, NdbClassSchemaChangeComparerByVersion> ClassSchemaChanges;
        // 程序启动时保存所有类的所有版本的变化。Key是类Id
        std::unordered_map<ClassId, ClassSchemaChanges, ClassIdHasher> m_allClassSchemaChanges;

        gcmp::OwnerPtr<NdbClassSchemaLibraryOfFile> m_opClassSchemaLibraryOfFile;
        HighPerformanceMutex m_criticalSectionLock;

    };
}

#pragma warning( pop )

